﻿#pragma once

#include "../util/spsc_queue.hpp"
#include <atomic>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include "../util/box_std_allocator.hh"

namespace kratos {
namespace service {
class ServiceBox;
}
} // namespace kratos

using BundleFiles = kratos::service::PoolVector<std::string>;
/**
 * Bundle信息.
 */
struct BundleInfo {
  std::string uuid; ///< 服务UUID
  std::string name; ///< 名字或本地路径
};

using BundleInfoVector = kratos::service::PoolVector<BundleInfo>;

namespace kratos {
namespace service {
/**
 * 服务下载器.
 */
class ServiceHttpLoader {
  corelib::SPSCQueue<BundleInfo> worker_to_main_;   ///< 事件队列

  std::atomic_bool running_{false};                 ///< 运行标志
  std::thread      libcurl_thread_;                 ///< 下载线程
  ServiceBox*      box_{nullptr};                   ///< ServiceBox
  std::mutex       mutex_;                          ///< 配置锁
  std::string      service_dir_;                    ///< 本地服务目录
  std::string      version_api_;                    ///< 版本API
  std::string      remote_service_repo_dir_;        ///< 远程服务仓库地址
  std::string      temp_service_dir_;               ///< 本地服务临时目录
  int              remote_repo_check_interval_{60}; ///< 版本更新周期，秒
  bool             is_open_remote_update_{false};   ///< 是否开启远程更新
  BundleInfoVector worker_only_infos_;              ///< 工作线程内使用的版本信息
  BundleInfoVector main_only_infos_;                ///< 逻辑线程内使用的版本信息
  bool             update_once_{false};             ///< 进行一次手动更新

public:
  /**
   * 构造.
   * 
   */
  ServiceHttpLoader();
  /**
   * 析构.
   * 
   */
  ~ServiceHttpLoader();
  // 启动
  // @param box 服务容器
  // @retval true 成功
  // @retval false 失败
  auto start(ServiceBox *box) -> bool;
  // 进行一次版本升级
  auto force_update() -> void;
  // 关闭
  // @retval true 成功
  // @retval false 失败
  auto stop() -> bool;
  // 逻辑主循环调用
  auto update() -> void;
  /**
   * 运行一次更新循环
   * \return 本次已更新（可能失败）的bundle数量
   */
  auto update_once()->std::size_t;
  /**
   * 检查某个版本是否已经存在
   *
   * \param box 服务容器
   * \param version 需要检查的版本
   * \return true存在，false不存在
   */
  static auto check_version_exists(
    service::ServiceBox* box,
    const std::string& version)->bool;

private:
  /**
   * 启动工作线程
   */
  auto start_worker()->void;
  /**
   * 循环检查更新
   * \param [IN][OUT] tick 自上次检查到现在的间隔, 毫秒
   */
  auto check_update_forever(std::time_t& tick)->void;
  /**
   * 检查一次更新 
   */
  auto check_update_once()->void;
  /**
   * 更新配置 
   */
  auto update_config()->void;
  /**
   * 通知主线程进行更新 
   */
  auto notify_update()->void;
  // 启动时下载最新的版本
  auto download_lastest_bundles_startup() -> bool;
  // [工作线程]从http://version_api?version=xx获取版本API JSON
  // @param version_api 远程API
  // @param [OUT]bundle_infos 远程API返回的需要更新的服务信息
  // @param version 需要拉取的版本号
  // @retval true 成功
  // @retval false 失败
  auto get_version_file(
    const std::string &version_api,
    BundleInfoVector &bundle_infos,
    std::string &version) -> bool;
  // [工作线程]从http://bundle_file_url获取版本bundle文件
  // @param file_path bundle存储路径
  // @param bundle_file_url 远程路径
  // @retval true 成功
  // @retval false 失败
  auto get_bundle_file(
    const std::string &file_path,
    const std::string &bundle_file_url) -> bool;
  // [工作线程]下载多个bundle文件
  // @param bundle_infos bundle信息数组
  // @param version_root bundle文件所在的本地根目录
  // @param version 版本
  // @param remote_service_repo_dir 远程仓库
  // @retval true 成功
  // @retval false 失败
  auto download_bundle_files(
    const BundleInfoVector &bundle_infos,
    const std::string &version_root,
    const std::string &version,
    const std::string &remote_service_repo_dir) -> bool;
  // [工作线程]检查版本是否已经下载并更新完成
  // @param version_root bundle文件所在的本地根目录
  // @param version 版本
  // @retval true 成功
  // @retval false 失败
  auto check_version(
    const std::string &version_root,
    const std::string &version) -> bool;
  // [工作线程]拉取所有bundle文件
  // @param bundle_infos bundle信息数组
  // @param temp_service_dir 存放bundle的临时目录
  // @param remote_service_repo_dir 远程仓库
  // @param version_root bundle文件所在的本地根目录
  // @param version 版本
  // @retval true 成功
  // @retval false 失败
  auto pull_all(
    const BundleInfoVector &bundle_infos,
    const std::string &temp_service_dir,
    const std::string &remote_service_repo_dir,
    const std::string &version_root, const std::string &version) -> bool;
  // [工作线程]完成更新，并建立版本done文件
  // @param version_root bundle文件所在的本地根目录
  // @param version 版本
  // @param bundle_infos bundle信息数组
  // @retval true 成功
  // @retval false 失败
  auto finish_update(
    const std::string &version_root,
    const std::string &version,
    const BundleInfoVector &bundle_infos) -> bool;
  // [工作线程]获取更新信息
  // @param service_dir 本地bundle存储目录
  // @param version_api 版本文件API
  // @param remote_service_repo_dir 远程仓库
  // @param temp_service_dir 存放bundle的临时目录
  // @retval true 成功
  // @retval false 失败
  auto get_info(
    std::string &service_dir,
    std::string &version_api,
    std::string &remote_service_repo_dir,
    std::string &temp_service_dir) -> bool;
  // [工作线程]拉取版本相关文件
  auto do_update() -> void;
};
} // namespace service
} // namespace kratos
